import 'dart:math';
import 'package:sleep/db/export.dart';
import 'package:sleep/single/analyze/base_analyze.dart';
import 'package:sleep/single/filter/base_filter.dart';
import 'package:sleep/single/find_peak.dart';
import 'package:sleep/single/point_model.dart';
import 'package:sleep/single/wave_show_model.dart';

class pulseAnalyze extends BaseAnayze {
  @override
  int get currentIndex => _currentIndex;

  @override
  List<double> get points => _showData;

  @override
  List<PluseShowModel> get waves => _showModes;

  PluseShowModel? get wave {
    return showModel;
  }

  List<double> _showData = [];
  List<double> _orData = []; //原始数据

  List<PluseShowModel> _showModes = [];
  PluseShowModel? showModel;

  int _currentIndex = 0;
  bool turnOver = false;

  pulseAnalyze(
      {required List<BaseFliter> fliter, required List<BaseFliter> showFliter})
      : super(fliter, showFliter);

  //定时给数据
  @override
  insertData(List<double> data) {
    timeIndex += 1;
    final filterData = toFilter(data);
    _showData.addAll(toShowFilter(data));
    hasTurnOff(data);
    _orData.addAll(filterData);
    _currentIndex += filterData.length;
    realtimeData(_orData.length >= 3000
        ? _orData.sublist(_orData.length - 3000)
        : _orData);

    Export.share().save(filterData, CsvType.pluseFatigue);
    Export.share().save(filterData, CsvType.pluseSleep);
  }

  //计算实时数据
  int timeIndex = 0;
  realtimeData(List<double> data) {
    //1s刷新一次
    if (timeIndex < 4) return;
    timeIndex = 0;
    List<PulseWaveModel> waves = pulseWaveFind().findWave(data, 0);
    showModel = PluseShowModel.fromWave(waves);
    if (showModel == null) return;
    _showModes.add(showModel!);
    calulateHrv();

    Export.share()
        .save(null, CsvType.pluseFatigueWave, dataStr: [showModel!.toFValue()]);
    Export.share().save(null, CsvType.pluseSleepWave,
        dataStr: [showModel!.toSleepValue()]);

    clearOverData();
  }

  clearOverData() {
    if (_showData.length > 4000) {
      _showData = _showData.sublist(_showData.length - 4000);
    }
    if (_orData.length > 4000) {
      _orData = _orData.sublist(_orData.length - 4000);
    }
    if (_showModes.length > 4000) {
      _showModes = _showModes.sublist(_showModes.length - 4000);
    }
  }

  calulateHrv() {
    List<double> waveTimes = [];
    for (int i = 0; i < _showModes.length; i++) {
      final current = _showModes[i];
      waveTimes.add(current.time);
      current.hrv ??= waveTimes.standardDeviation();
      if (waveTimes.length > 600) waveTimes.removeAt(0);
    }
  }

  hasTurnOff(List<double> data) {
    bool hasTurnoff = data.where((e) => e > 3510).isNotEmpty;
    turnOver = hasTurnoff;
  }

  @override
  void clear() {
    _showData.clear();
    _orData.clear();
    _showModes.clear();
    _currentIndex = 0;
  }
}

class pulseWaveFind {
  pulseWaveFind();

  List<PulseWaveModel> findWave(List<double> data, double startIndex) {
    List<PulseWaveModel> waves = [];

    //找到波峰波谷
    List<WavePoint> peaks = PeakFind.findPeaks(data);

    List<WavePoint> secondPeakWave = PeakFind.findPeaks(
        peaks.map((e) => e.y).toList(),
        halfwindowSize: 3); //二次波峰波谷

    for (int i = 0; i < secondPeakWave.length; i++) {
      final e = secondPeakWave[i];
      final index = e.x.toInt();
      WaveType? type;
      if (e.type == WaveType.peak) type = WaveType.topPeak;
      if (type != null) {
        final replacePeak = peaks[index];
        peaks.replaceRange(
            index, index + 1, [e.copyWith(type: type, x: replacePeak.x)]);

        final preIndex = index - 1;
        if (preIndex > 0) {
          final prePeak = peaks[preIndex];
          WaveType? type = prePeak.type == WaveType.valley
              ? WaveType.topValley
              : prePeak.type;
          peaks.replaceRange(
              preIndex, preIndex + 1, [prePeak.copyWith(type: type)]);
        }
      }
    }

    List<WavePoint> points = [];
    for (int i = 0; i < peaks.length; i++) {
      final peak = peaks[i];

      if (peak.type == WaveType.topValley) {
        final pm = getPm(data, startIndex, points);
        final wave = PulseWaveModel(
            [...points, peak.copyWith(x: peak.x + startIndex)], pm);
        waves.add(wave);

        points.clear();
        points.add(peak.copyWith(x: peak.x + startIndex));
      } else {
        points.add(peak.copyWith(x: peak.x + startIndex));
      }
    }
    return waves;
  }

  double getPm(
      List<double> data, double startIndex, List<WavePoint> peakPoints) {
    final pointStart = peakPoints.first.x.toInt() - startIndex.toInt();
    final pointEnd = peakPoints.last.x.toInt() - startIndex.toInt();

    final wavePoints = data.sublist(pointStart, pointEnd);

    final minV = peakPoints
        .map((e) => e.y)
        .reduce((value, element) => min(value, element));

    return wavePoints.reduce((value, element) => value + element - minV * 2) /
        wavePoints.length;
  }
}

extension E on List<double> {
  double standardDeviation() {
    if (length <= 1) return 0;
    final mean = reduce((double a, double b) => a + b) / length;
    num variance = map((x) => pow(x - mean, 2)).reduce((a, b) => a + b);
    double standardDeviation = sqrt(variance / length);
    return standardDeviation;
  }
}
